﻿<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<title>Path Observer Tests</title>
		
		<link href="../Common/Styles/qunit.css" type="text/css" rel="stylesheet" />
		
		<script src="../Common/Scripts/JQuery/jquery-1.3.2.js" type="text/javascript"></script>
		<script src="../Common/Scripts/QUnit/qunit.js" type="text/javascript"></script>
		<script src="../Common/Scripts/Microsoft/MicrosoftAjax.debug.js" type="text/javascript"></script>
		<script src="../Common/Scripts/ExoWeb/exoweb.js" type="text/javascript"></script>
		<script src="../Common/Scripts/QUnit/qunit.ext.js" type="text/javascript"></script>
		<script src="../Common/Scripts/mock-driver.js" type="text/javascript"></script>
		
		<script type="text/javascript">
			test("addSpecificPropertyChanged", function() {
				var driver = getDriver("Joe", 30);

				var numCalls;
				var handler = function() { numCalls++; };

				Sys.Observer.addSpecificPropertyChanged(driver, "name", handler);

				numCalls = 0;
				Sys.Observer.setValue(driver, "name", "Bob");
				equals(driver.name, "Bob", "Name should be changed to Bob");
				equals(numCalls, 1, "Handler should have been called");

				numCalls = 0;
				Sys.Observer.setValue(driver, "age", 5);
				equals(driver.age, 5, "Age should be changed to 5");
				equals(numCalls, 0, "Handler should NOT have been called on a different property");

				Sys.Observer.removeSpecificPropertyChanged(driver, "name", handler);

				numCalls = 0;
				Sys.Observer.setValue(driver, "name", "Bill");
				equals(driver.name, "Bill", "Name should be changed to Bill");
				equals(numCalls, 0, "Handler should NOT have been called after removing");
			});

			test("PropertyObserver - start", function () {
				var source = { foo: "bar" };

				var observer = new ExoWeb.PropertyObserver("foo");

				var numCalls = 0;
				observer.start(source, function () { numCalls++; });

				numCalls = 0;
				Sys.Observer.setValue(source, "foo", "foo");
				equals(numCalls, 1, "Handler should be called after setting property value");
			});

			test("PropertyObserver - start (Array)", function () {
				var source = { foo: ["a", "b"] };
				Sys.Observer.makeObservable(source.foo);

				var observer = new ExoWeb.PropertyObserver("foo");

				var numCalls = 0;
				observer.start(source, function () { numCalls++; });

				numCalls = 0;
				source.foo.remove(source.foo[1]);
				equals(numCalls, 1, "Handler should be called after removing item");

				numCalls = 0;
				source.foo.add("c");
				equals(numCalls, 1, "Handler should be called after adding item");

				numCalls = 0;
				Sys.Observer.setValue(source, "foo", []);
				equals(numCalls, 1, "Handler should be called after resetting array property value");
			});

			test("PropertyObserver - stop", function () {
				var source = { foo: "bar" };

				var observer = new ExoWeb.PropertyObserver("foo");

				var numCalls = 0;
				observer.start(source, function () { numCalls++; });

				// stop observing immediately
				observer.stop();

				numCalls = 0;
				Sys.Observer.setValue(source, "foo", "foo");
				equals(numCalls, 0, "Handler should NOT be called since the observer is stopped");
			});

			test("PropertyObserver - stop (Array)", function () {
				var source = { foo: [] };
				Sys.Observer.makeObservable(source.foo);

				var observer = new ExoWeb.PropertyObserver("foo");

				var numCalls = 0;
				observer.start(source, function () { numCalls++; });

				var originalFoo = source.foo;
				var newFoo = ["a", "b"];
				Sys.Observer.makeObservable(newFoo);
				Sys.Observer.setValue(source, "foo", newFoo);

				numCalls = 0;
				originalFoo.add("d");
				equals(numCalls, 0, "Handler should NOT be called after adding item to original array since value since the array property value has been changed");

				numCalls = 0;
				originalFoo.remove("d");
				equals(numCalls, 0, "Handler should NOT be called after removing item from original array since the array property value has been changed");

				// stop observing immediately
				observer.stop();

				numCalls = 0;
				source.foo.remove(source.foo[1]);
				equals(numCalls, 0, "Handler should NOT be called after removing item since the observer is stopped");

				numCalls = 0;
				source.foo.add("c");
				equals(numCalls, 0, "Handler should NOT be called after adding item since the observer is stopped");

				numCalls = 0;
				Sys.Observer.setValue(source, "foo", originalFoo);
				equals(numCalls, 0, "Handler should NOT be called after resetting array property value since the observer is stopped");
			});

			test("addPathChanged - One Property", function() {
				var driver = getDriver("Joe", 30);

				var numCalls;
				var handler = function() { numCalls++; };

				Sys.Observer.addPathChanged(driver, "name", handler);

				numCalls = 0;
				Sys.Observer.setValue(driver, "name", "Bob");
				equals(driver.name, "Bob", "Name should be changed to Bob");
				equals(numCalls, 1, "Handler should have been called");

				numCalls = 0;
				Sys.Observer.setValue(driver, "age", 100);
				equals(driver.age, 100, "Age should be changed to 100");
				equals(numCalls, 0, "Handler should NOT have been called for a property ouside of the path");

				// Add a separate handler to the same source and path.
				var otherNumCalls = 0;
				Sys.Observer.addPathChanged(driver, "name", function() { otherNumCalls++; });

				// Remove the original handler.
				Sys.Observer.removePathChanged(driver, "name", handler);

				numCalls = 0;
				Sys.Observer.setValue(driver, "name", "Joe");
				equals(driver.name, "Joe", "Name should be changed to Joe");
				equals(numCalls, 0, "Handler should NOT have been called after removing");
				equals(otherNumCalls, 1, "Second handler should have been called even after removing the first handler");
			});

			test("addPathChanged - Multiple Properties", function() {
				var driver = getDriver("Joe", 30, getCar("Ford"), getOwner("Larry", 40));

				var numCalls;
				var handler = function() { numCalls++; };

				Sys.Observer.addPathChanged(driver, "car.owner.name", handler);

				numCalls = 0;
				Sys.Observer.setValue(driver.car.owner, "name", "Curly");
				equals(driver.car.owner.name, "Curly", "Name should be changed to Curly");
				equals(numCalls, 1, "Handler should have been called");
				
				numCalls = 0;
				Sys.Observer.setValue(driver.car, "owner", getOwner("Moe", 35));
				equals(driver.car.owner.name, "Moe", "New owner should be Moe");
				equals(numCalls, 1, "Handler should have been called");

				Sys.Observer.removePathChanged(driver, "car.owner.name", handler);

				numCalls = 0;
				Sys.Observer.setValue(driver.car, "owner", getOwner("Larry", 40));
				equals(driver.car.owner.name, "Larry", "Owner should be Larry");
				equals(numCalls, 0, "Handler should NOT have been called after removing");
			});

			test("addPathChanged - Multiple Properties - Initial Nulls", function() {
				var driver = getDriver("Joe", 30);

				var numCalls;
				var handler = function() { numCalls++; };

				Sys.Observer.addPathChanged(driver, "car.owner.name", handler);

				numCalls = 0;
				Sys.Observer.setValue(driver, "car", getCar("Chevy"));
				equals(driver.car.make, "Chevy", "Car should be a Chevy");
				equals(numCalls, 1, "Handler should have been called");

				numCalls = 0;
				Sys.Observer.setValue(driver.car, "owner", getOwner("Larry", 40, driver.car));
				equals(driver.car.owner.name, "Larry", "Owner should be Larry");
				equals(numCalls, 1, "Handler should have been called");

				numCalls = 0;
				Sys.Observer.setValue(driver.car.owner, "name", "Curly");
				equals(driver.car.owner.name, "Curly", "Name should be changed to Curly");
				equals(numCalls, 1, "Handler should have been called");
			});

			test("addPathChanged - Multiple Properties - Split Path", function() {
				var driver = getDriver("Joe", 30, getCar("Ford"), getOwner("Larry", 40));

				var numCalls;
				var handler = function() { numCalls++; };

				Sys.Observer.addPathChanged(driver, "car.owner.name", handler);

				var ford = driver.car;
				equals(driver.car.make, "Ford", "Car should be a Ford");

				numCalls = 0;
				var chevy = getCar("Chevy", driver.car.owner);
				Sys.Observer.setValue(driver, "car", chevy);
				equals(driver.car.make, "Chevy", "Car should be a Chevy");
				equals(numCalls, 1, "Handler should have been called");

				numCalls = 0;
				Array.remove(ford.owner.cars, ford);
				Sys.Observer.setValue(ford, "owner", getOwner("Curly", 40, ford));
				equals(numCalls, 0, "Handler should NOT have been called on an object that used to be a part of the path");

				numCalls = 0;
				Sys.Observer.setValue(driver.car.owner, "name", "Moe");
				equals(driver.car.owner.name, "Moe", "Name should be changed to Moe");
				equals(numCalls, 1, "Handler should have been called");
			});

			test("addPathChanged - Array", function () {
				var driver1 = getDriver("Joe", 30, getCar("Ford"), getOwner("Larry", 40));
				var driver2 = getDriver("Bob", 22, getCar("Chevy"), getOwner("Moe", 35));

				var drivers = [driver1, driver2];
				Sys.Observer.makeObservable(drivers);

				var numCalls;
				var handler = function () { numCalls++; };

				Sys.Observer.addPathChanged(drivers, "car.owner.name", handler);

				var ford = driver1.car;
				equals(driver1.car.make, "Ford", "Car should be a Ford");

				// ensure that path changed handles an array as the root
				numCalls = 0;
				var chevy = getCar("Chevy", driver1.car.owner);
				Sys.Observer.setValue(driver1, "car", chevy);
				equals(driver1.car.make, "Chevy", "Car should be a Chevy");
				equals(numCalls, 1, "Handler should have been called");

				var container = { drivers: drivers };

				var numCalls2 = 0;
				var handler2 = function () { numCalls2++; };

				Sys.Observer.addPathChanged(container, "drivers.car.owner.name", handler2);

				// ensure that path changed handle an array in the path
				numCalls2 = 0;
				equals(container.drivers.length, 2, "Should be two drivers in the list");
				container.drivers.remove(driver1);
				equals(container.drivers.length, 1, "Should be one driver in the list after removing");
				equals(numCalls2, 1, "Handler should have been called because of remove from array");
			});
		</script>
	</head>
	<body xmlns:sys="javascript:Sys" xmlns:dataview="javascript:Sys.UI.DataView" sys:activate="*">
		
		<!-- QUnit Display -->
		<h1 id="qunit-header">Test Results:</h1>
		<h2 id="qunit-banner"></h2>
		<h2 id="qunit-userAgent"></h2>
		<ol id="qunit-tests"></ol>
	</body>
</html>
